package org.bookshare.document.beans;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.benetech.event.EventListener;
import org.benetech.exception.WrappedException;
import org.benetech.util.ViolationCollatingErrorHandler;
import org.benetech.util.ZipContents;
import org.bookshare.document.daisy.OPFPackageFile;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;

/**
 * Open Ebook.
 * @author Reuben Firmin
 */
public class OEBDocumentSet extends AbstractDocumentSet<DocumentType.OEB> implements HasOPF {

	private ZipContents zipContents;

	/**
	 * Construct an Open Ebook DocumentSet.
	 * @param zipContents The zip
	 * @param fileBasePath The tmp directory to extract to
	 * @param eventListener The event listener
	 * @param errorHandler The error handler
	 * @throws IOException if file access fails
	 */
	public OEBDocumentSet(final ZipContents zipContents, final File fileBasePath,
			final EventListener eventListener, final ViolationCollatingErrorHandler errorHandler)
	throws IOException
	{
    	super(fileBasePath, DocumentType.OEB, eventListener, errorHandler, false);
    	this.zipContents = zipContents;
        for (DocumentComponent element : DocumentType.OEB.getComponents()) {
            addFiles(element, zipContents.getFilesMatchingPattern(element.getFileRegexp()));
        }
	}

	/**
	 * {@inheritDoc}
	 */
    public final Document getOPFDocument() {
        return getFactory().getOPFDocument();
    }

    /**
     * Get the HTML documents associated with this set. Returns them in spine order.
     * @return never null
     * @throws JDOMException if the OPF couldn't be parsed
     * @throws IOException if the OPF couldn't be parsed
     */
    public final List<Document> getHTMLDocuments()
    throws JDOMException, IOException
    {
    	final OPFPackageFile opfManifest = getOPFPackageFile();
    	if (opfManifest == null) {
    		throw new IOException("No OPF File included.");
    	}
    	final Map<String, Element> manifestMap = opfManifest.getManifestMap();
    	final Map<String, Element> spineMap = opfManifest.getSpineMap();
    	final List<String> fileHrefs = new ArrayList<String>();
    	for (String id : spineMap.keySet()) {
    		final Element manifestItem = manifestMap.get(id);
    		if (manifestItem != null) {
    			// XXX should we care about missing hrefs??
    			final Attribute href = manifestItem.getAttribute("href");
    			if (href != null) {
    				fileHrefs.add(href.getValue());
    			}
    		}
    	}

    	createDocuments(DocumentComponent.HTML, false);
    	final List<Document> htmlDocs = new ArrayList<Document>();
    	for (String href : fileHrefs) {
    		final Document doc = getDocument(href);
    		if (doc != null) {
    			htmlDocs.add(doc);
    		}
    	}
        return htmlDocs;
    }

    /**
     * {@inheritDoc}
     */
    public final File getOPFFile() {
        return getFactory().getOPFFile();
    }

    /**
     * Get the XML files associated with this set.
     * @return never null
     */
    public final List<File> getHTMLFiles() {
        return getFiles(DocumentComponent.HTML);
    }

	/**
	 * {@inheritDoc}
	 */
	public final String getName()
		throws IOException
	{
		try {
			return getOPFPackageFile().getBaseName();
		} catch (JDOMException e) {
			return null;
		}
	}

	public OPFPackageFile getOPFPackageFile() throws JDOMException, IOException {
		return getFactory().getOPFPackageFile();
	}

	public List<File> getOtherFiles() {
		return new ArrayList<File>();
	}

    /**
     * Whether a file is contained within the document set.
     * @param fileHref The name of the file.
     * @return True if the file is in the set
     */
    public boolean containsFile(final String fileHref) throws IOException {
    	try {
    		return zipContents.contains(getOPFPackageFile().getFile().getAbsolutePath()	+ File.separator + fileHref);
    	} catch (JDOMException e) {
    		throw new WrappedException(e);
    	}
    }

    /**
     * Whether a file is contained within the document set with ignored case.
     * @param fileHref The name of the file.
     * @return True if the file is in the set
     */

    public boolean containsFileIgnoreCase(final String fileHref) throws IOException {
    	try {
    		return zipContents.containsIgnoreCase(getOPFPackageFile().getFile().getAbsolutePath()
        		+ File.separator + fileHref);
    	} catch (JDOMException e) {
    		throw new WrappedException(e);
    	}
    }
}
